package asteroids.model.programs.parsing.statements;

import java.util.List;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import be.kuleuven.cs.som.annotate.Basic;

import asteroids.model.Ship;
import asteroids.model.Bullet;
import asteroids.model.Asteroid;
import asteroids.model.SpaceObject;
import asteroids.model.programs.parsing.ProgramFactory.ForeachType;
import asteroids.model.programs.parsing.types.EntityLiteral;
import asteroids.model.programs.parsing.types.Type;

/**
 * A class of foreach statements.
 * A foreach statement iterates trough a certain list and as long there are elements in the list, it executes a statement.
 * 
 * @author Thijs Dieltjens - Wim Konings
 * @version 2.0
 */
public class ForeachStatement extends Statement {

	public ForeachStatement(asteroids.model.programs.parsing.ProgramFactory.ForeachType type,
			String variableName, Statement body,int line, int column) {
		super(line, column);
		this.foreachType = type;		
		this.variableName = variableName;
		this.body=body;
	}

	@Basic
	public String getVariableName() {
		return variableName;
	}

	private String variableName;

	@Basic
	public ForeachType getForeachType() {
		return foreachType;
	}

	private ForeachType foreachType;

	@Basic
	public Statement getBody() {
		return body;
	}

	private Statement body;

	@Override
	public void execute(Ship ship) {
		Set<? extends SpaceObject> result = new HashSet<SpaceObject>();

		if(getForeachType() == ForeachType.ANY){	
			result = ship.getWorld().getAllSpaceObjects();
		}
		else if(getForeachType() == ForeachType.SHIP){
			result = ship.getWorld().getSpaceObjectsOfType(Ship.class);
		}
		else if (getForeachType() == ForeachType.BULLET){
			result = ship.getWorld().getSpaceObjectsOfType(Bullet.class);
		}
		else if(getForeachType() == ForeachType.ASTEROID){
			result = ship.getWorld().getSpaceObjectsOfType(Asteroid.class);
		}
		else{
			// Do nothing
		}
		for(SpaceObject so2: result){
			ship.getProgram().addGlobalVariable(getVariableName(), new EntityLiteral(so2));
			getBody().execute(ship);			
		}
	}

	@Override
	public List<String> getTypeCheckErrors(List<String> errors, Map<String, Type> globals) {
		if(containsActionStatement()){
			errors.add(getLine()+"-" +getColumn()+" : There can be no action in a foreach loop");
		}
		if(!globals.containsKey(getVariableName())){
			errors.add(getLine()+"-" +getColumn()+" : The variable name '"+ getVariableName() +"' should have already been defined as global variable.");
		}
		return errors;
	}

	@Override
	public boolean containsActionStatement() {
		return getBody().containsActionStatement();
	}
}
